-- ALU Architecture
-- Chang Lan, <changlan9@gmail.com>
-- 11/9/2011

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

architecture behave of alu is

begin
    process(a, b, opcode)
        -- declare variables
        variable a_uns : unsigned(width-1 downto 0);
        variable b_uns : unsigned(width-1 downto 0);
        variable r_uns : unsigned(width-1 downto 0);
        
    begin
        -- init
        a_uns    := unsigned(a);
        b_uns    := unsigned(b);
        r_uns    := (others => '0');

        -- choose desired operation
        case opcode is
            -- add
            when "0000" =>
                r_uns := a_uns + b_uns;

                -- sub
            when "0001" =>
                r_uns := a_uns - b_uns;

                -- and
            when "0010" =>
                r_uns := a_uns and b_uns;

                -- or
            when "0011" =>
                r_uns := a_uns or b_uns;

                -- xor
            when "0100" =>
                r_uns := a_uns xor b_uns;

                -- cmpu
            when "0101" =>
                if a_uns < b_uns then
                    r_uns := TO_UNSIGNED(1, r_uns'length);
                else
                    r_uns := (others => '0');
                end if;

                -- cmp
            when "0110" =>
                if signed(a) < signed(b) then
                    r_uns := TO_UNSIGNED(1, r_uns'length);
                else
                    r_uns := (others => '0');
                end if;

                -- sll
            when "0111" =>
                r_uns := SHIFT_LEFT(a_uns, TO_INTEGER(b_uns));

                -- sra
            when "1000" =>
                r_uns := unsigned(SHIFT_RIGHT(signed(a), TO_INTEGER(b_uns)));

                -- srl
            when "1001" =>
                r_uns := SHIFT_RIGHT(a_uns, TO_INTEGER(b_uns));

                -- not
            when "1010" =>
                r_uns := not a_uns;

                -- neg
            when "1011" =>
                r_uns := 0 - a_uns;

                -- r1
            when "1100" =>
                r_uns := a_uns;

                -- r2
            when "1101" =>
                r_uns := b_uns;

				-- sub2
			when "1110" =>
                r_uns := a_uns - 2;
				
                -- others
            when others =>
                r_uns := (others => 'X');
        end case;


        -- assign variables to output signals
        result <= std_ulogic_vector(r_uns);
        
    end process;

end behave;
